home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / util / HashMap.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  25.5 KB  |  808 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)HashMap.java    1.26 98/09/30
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util;
  16. import java.io.*;
  17.  
  18. /**
  19.  * Hash table based implementation of the <tt>Map</tt> interface.  This
  20.  * implementation provides all of the optional map operations, and permits
  21.  * <tt>null</tt> values and the <tt>null</tt> key.  (The <tt>HashMap</tt>
  22.  * class is roughly equivalent to <tt>Hashtable</tt>, except that it is
  23.  * unsynchronized and permits nulls.)  This class makes no guarantees as to
  24.  * the order of the map; in particular, it does not guarantee that the order
  25.  * will remain constant over time.<p>
  26.  *
  27.  * This implementation provides constant-time performance for the basic
  28.  * operations (<tt>get</tt> and <tt>put</tt>), assuming the hash function
  29.  * disperses the elements properly among the buckets.  Iteration over
  30.  * collection views requires time proportional to the "capacity" of the
  31.  * <tt>HashMap</tt> instance (the number of buckets) plus its size (the number
  32.  * of key-value mappings).  Thus, it's very important not to set the intial
  33.  * capacity too high (or the load factor too low) if iteration performance is
  34.  * important.<p>
  35.  *
  36.  * An instance of <tt>HashMap</tt> has two parameters that affect its
  37.  * performance: <i>initial capacity</i> and <i>load factor</i>.  The
  38.  * <i>capacity</i> is the number of buckets in the hash table, and the initial
  39.  * capacity is simply the capacity at the time the hash table is created.  The
  40.  * <i>load factor</i> is a measure of how full the hash table is allowed to
  41.  * get before its capacity is automatically increased.  When the number of
  42.  * entries in the hash table exceeds the product of the load factor and the
  43.  * current capacity, the capacity is roughly doubled by calling the
  44.  * <tt>rehash</tt> method.<p>
  45.  *
  46.  * As a general rule, te default load factor (.75) offers a good tradeoff
  47.  * between time and space costs.  Higher values decrease the space overhead
  48.  * but increase the lookup cost (reflected in most of the operations of the
  49.  * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>).  The
  50.  * expected number of entries in the map and its load factor should be taken
  51.  * into account when setting its initial capacity, so as to minimize the
  52.  * number of <tt>rehash</tt> operations.  If the initial capacity is greater
  53.  * than the maximum number of entries divided by the load factor, no
  54.  * <tt>rehash</tt> operations will ever occur.<p>
  55.  *
  56.  * If many mappings are to be stored in a <tt>HashMap</tt> instance, creating
  57.  * it with a sufficiently large capacity will allow the mappings to be stored
  58.  * more efficiently than letting it perform automatic rehashing as needed to
  59.  * grow the table.<p>
  60.  *
  61.  * <b>Note that this implementation is not synchronized.</b> If multiple
  62.  * threads access this map concurrently, and at least one of the threads
  63.  * modifies the map structurally, it <i>must</i> be synchronized externally.
  64.  * (A structural modification is any operation that adds or deletes one or
  65.  * more mappings; merely changing the value associated with a key that an
  66.  * instance already contains is not a structural modification.)  This is
  67.  * typically accomplished by synchronizing on some object that naturally
  68.  * encapsulates the map.  If no such object exists, the map should be
  69.  * "wrapped" using the <tt>Collections.synchronizedMap</tt> method.  This is
  70.  * best done at creation time, to prevent accidental unsynchronized access to
  71.  * the map: <pre> Map m = Collections.synchronizedMap(new HashMap(...));
  72.  * </pre><p>
  73.  *
  74.  * The iterators returned by all of this class's "collection view methods" are
  75.  * <i>fail-fast</i>: if the map is structurally modified at any time after the
  76.  * iterator is created, in any way except through the iterator's own
  77.  * <tt>remove</tt> or <tt>add</tt> methods, the iterator will throw a
  78.  * <tt>ConcurrentModificationException</tt>.  Thus, in the face of concurrent
  79.  * modification, the iterator fails quickly and cleanly, rather than risking
  80.  * arbitrary, non-deterministic behavior at an undetermined time in the
  81.  * future.
  82.  *
  83.  * @author  Josh Bloch
  84.  * @author  Arthur van Hoff
  85.  * @version 1.26, 09/30/98
  86.  * @see     Object#hashCode()
  87.  * @see     Collection
  88.  * @see        Map
  89.  * @see        TreeMap
  90.  * @see        Hashtable
  91.  * @since JDK1.2
  92.  */
  93.  
  94. public class HashMap extends AbstractMap implements Map, Cloneable,
  95.                      java.io.Serializable {
  96.     /**
  97.      * The hash table data.
  98.      */
  99.     private transient Entry table[];
  100.  
  101.     /**
  102.      * The total number of mappings in the hash table.
  103.      */
  104.     private transient int count;
  105.  
  106.     /**
  107.      * The table is rehashed when its size exceeds this threshold.  (The
  108.      * value of this field is (int)(capacity * loadFactor).)
  109.      *
  110.      * @serial
  111.      */
  112.     private int threshold;
  113.  
  114.     /**
  115.      * The load factor for the hashtable.
  116.      *
  117.      * @serial
  118.      */
  119.     private float loadFactor;
  120.  
  121.     /**
  122.      * The number of times this HashMap has been structurally modified
  123.      * Structural modifications are those that change the number of mappings in
  124.      * the HashMap or otherwise modify its internal structure (e.g.,
  125.      * rehash).  This field is used to make iterators on Collection-views of
  126.      * the HashMap fail-fast.  (See ConcurrentModificationException).
  127.      */
  128.     private transient int modCount = 0;
  129.  
  130.     /**
  131.      * Constructs a new, empty map with the specified initial 
  132.      * capacity and the specified load factor. 
  133.      *
  134.      * @param      initialCapacity   the initial capacity of the HashMap.
  135.      * @param      loadFactor        the load factor of the HashMap
  136.      * @throws     IllegalArgumentException  if the initial capacity is less
  137.      *               than zero, or if the load factor is nonpositive.
  138.      */
  139.     public HashMap(int initialCapacity, float loadFactor) {
  140.     if (initialCapacity < 0)
  141.         throw new IllegalArgumentException("Illegal Initial Capacity: "+
  142.                                                initialCapacity);
  143.         if (loadFactor <= 0)
  144.             throw new IllegalArgumentException("Illegal Load factor: "+
  145.                                                loadFactor);
  146.         if (initialCapacity==0)
  147.             initialCapacity = 1;
  148.     this.loadFactor = loadFactor;
  149.     table = new Entry[initialCapacity];
  150.     threshold = (int)(initialCapacity * loadFactor);
  151.     }
  152.  
  153.     /**
  154.      * Constructs a new, empty map with the specified initial capacity
  155.      * and default load factor, which is <tt>0.75</tt>.
  156.      *
  157.      * @param   initialCapacity   the initial capacity of the HashMap.
  158.      * @throws    IllegalArgumentException if the initial capacity is less
  159.      *              than zero.
  160.      */
  161.     public HashMap(int initialCapacity) {
  162.     this(initialCapacity, 0.75f);
  163.     }
  164.  
  165.     /**
  166.      * Constructs a new, empty map with a default capacity and load
  167.      * factor, which is <tt>0.75</tt>.
  168.      */
  169.     public HashMap() {
  170.     this(101, 0.75f);
  171.     }
  172.  
  173.     /**
  174.      * Constructs a new map with the same mappings as the given map.  The
  175.      * map is created with a capacity of twice the number of mappings in
  176.      * the given map or 11 (whichever is greater), and a default load factor,
  177.      * which is <tt>0.75</tt>.
  178.      */
  179.     public HashMap(Map t) {
  180.     this(Math.max(2*t.size(), 11), 0.75f);
  181.     putAll(t);
  182.     }
  183.  
  184.     /**
  185.      * Returns the number of key-value mappings in this map.
  186.      *
  187.      * @return the number of key-value mappings in this map.
  188.      */
  189.     public int size() {
  190.     return count;
  191.     }
  192.  
  193.     /**
  194.      * Returns <tt>true</tt> if this map contains no key-value mappings.
  195.      *
  196.      * @return <tt>true</tt> if this map contains no key-value mappings.
  197.      */
  198.     public boolean isEmpty() {
  199.     return count == 0;
  200.     }
  201.  
  202.     /**
  203.      * Returns <tt>true</tt> if this map maps one or more keys to the
  204.      * specified value.
  205.      *
  206.      * @param value value whose presence in this map is to be tested.
  207.      * @return <tt>true</tt> if this map maps one or more keys to the
  208.      *         specified value.
  209.      */
  210.     public boolean containsValue(Object value) {
  211.     Entry tab[] = table;
  212.  
  213.     if (value==null) {
  214.         for (int i = tab.length ; i-- > 0 ;)
  215.         for (Entry e = tab[i] ; e != null ; e = e.next)
  216.             if (e.value==null)
  217.             return true;
  218.     } else {
  219.         for (int i = tab.length ; i-- > 0 ;)
  220.         for (Entry e = tab[i] ; e != null ; e = e.next)
  221.             if (value.equals(e.value))
  222.             return true;
  223.     }
  224.  
  225.     return false;
  226.     }
  227.  
  228.     /**
  229.      * Returns <tt>true</tt> if this map contains a mapping for the specified
  230.      * key.
  231.      * 
  232.      * @return <tt>true</tt> if this map contains a mapping for the specified
  233.      * key.
  234.      * @param key key whose presence in this Map is to be tested.
  235.      */
  236.     public boolean containsKey(Object key) {
  237.     Entry tab[] = table;
  238.         if (key != null) {
  239.             int hash = key.hashCode();
  240.             int index = (hash & 0x7FFFFFFF) % tab.length;
  241.             for (Entry e = tab[index]; e != null; e = e.next)
  242.                 if (e.hash==hash && key.equals(e.key))
  243.                     return true;
  244.         } else {
  245.             for (Entry e = tab[0]; e != null; e = e.next)
  246.                 if (e.key==null)
  247.                     return true;
  248.         }
  249.  
  250.     return false;
  251.     }
  252.  
  253.     /**
  254.      * Returns the value to which this map maps the specified key.  Returns
  255.      * <tt>null</tt> if the map contains no mapping for this key.  A return
  256.      * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
  257.      * map contains no mapping for the key; it's also possible that the map
  258.      * explicitly maps the key to <tt>null</tt>.  The <tt>containsKey</tt>
  259.      * operation may be used to distinguish these two cases.
  260.      *
  261.      * @return the value to which this map maps the specified key.
  262.      * @param key key whose associated value is to be returned.
  263.      */
  264.     public Object get(Object key) {
  265.     Entry tab[] = table;
  266.  
  267.         if (key != null) {
  268.             int hash = key.hashCode();
  269.             int index = (hash & 0x7FFFFFFF) % tab.length;
  270.             for (Entry e = tab[index]; e != null; e = e.next)
  271.                 if ((e.hash == hash) && key.equals(e.key))
  272.                     return e.value;
  273.     } else {
  274.             for (Entry e = tab[0]; e != null; e = e.next)
  275.                 if (e.key==null)
  276.                     return e.value;
  277.         }
  278.  
  279.     return null;
  280.     }
  281.  
  282.     /**
  283.      * Rehashes the contents of this map into a new <tt>HashMap</tt> instance
  284.      * with a larger capacity. This method is called automatically when the
  285.      * number of keys in this map exceeds its capacity and load factor.
  286.      */
  287.     private void rehash() {
  288.     int oldCapacity = table.length;
  289.     Entry oldMap[] = table;
  290.  
  291.     int newCapacity = oldCapacity * 2 + 1;
  292.     Entry newMap[] = new Entry[newCapacity];
  293.  
  294.     modCount++;
  295.     threshold = (int)(newCapacity * loadFactor);
  296.     table = newMap;
  297.  
  298.     for (int i = oldCapacity ; i-- > 0 ;) {
  299.         for (Entry old = oldMap[i] ; old != null ; ) {
  300.         Entry e = old;
  301.         old = old.next;
  302.  
  303.         int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  304.         e.next = newMap[index];
  305.         newMap[index] = e;
  306.         }
  307.     }
  308.     }
  309.  
  310.     /**
  311.      * Associates the specified value with the specified key in this map.
  312.      * If the map previously contained a mapping for this key, the old
  313.      * value is replaced.
  314.      *
  315.      * @param key key with which the specified value is to be associated.
  316.      * @param value value to be associated with the specified key.
  317.      * @return previous value associated with specified key, or <tt>null</tt>
  318.      *           if there was no mapping for key.  A <tt>null</tt> return can
  319.      *           also indicate that the HashMap previously associated
  320.      *           <tt>null</tt> with the specified key.
  321.      */
  322.     public Object put(Object key, Object value) {
  323.     // Makes sure the key is not already in the HashMap.
  324.     Entry tab[] = table;
  325.         int hash = 0;
  326.         int index = 0;
  327.  
  328.         if (key != null) {
  329.             hash = key.hashCode();
  330.             index = (hash & 0x7FFFFFFF) % tab.length;
  331.             for (Entry e = tab[index] ; e != null ; e = e.next) {
  332.                 if ((e.hash == hash) && key.equals(e.key)) {
  333.                     Object old = e.value;
  334.                     e.value = value;
  335.                     return old;
  336.                 }
  337.             }
  338.         } else {
  339.             for (Entry e = tab[0] ; e != null ; e = e.next) {
  340.                 if (e.key == null) {
  341.                     Object old = e.value;
  342.                     e.value = value;
  343.                     return old;
  344.                 }
  345.             }
  346.         }
  347.  
  348.     modCount++;
  349.     if (count >= threshold) {
  350.         // Rehash the table if the threshold is exceeded
  351.         rehash();
  352.  
  353.             tab = table;
  354.             index = (hash & 0x7FFFFFFF) % tab.length;
  355.     }
  356.  
  357.     // Creates the new entry.
  358.     Entry e = new Entry(hash, key, value, tab[index]);
  359.     tab[index] = e;
  360.     count++;
  361.     return null;
  362.     }
  363.  
  364.     /**
  365.      * Removes the mapping for this key from this map if present.
  366.      *
  367.      * @param key key whose mapping is to be removed from the map.
  368.      * @return previous value associated with specified key, or <tt>null</tt>
  369.      *           if there was no mapping for key.  A <tt>null</tt> return can
  370.      *           also indicate that the map previously associated <tt>null</tt>
  371.      *           with the specified key.
  372.      */
  373.     public Object remove(Object key) {
  374.     Entry tab[] = table;
  375.  
  376.         if (key != null) {
  377.             int hash = key.hashCode();
  378.             int index = (hash & 0x7FFFFFFF) % tab.length;
  379.  
  380.             for (Entry e = tab[index], prev = null; e != null;
  381.                  prev = e, e = e.next) {
  382.                 if ((e.hash == hash) && key.equals(e.key)) {
  383.                     modCount++;
  384.                     if (prev != null)
  385.                         prev.next = e.next;
  386.                     else
  387.                         tab[index] = e.next;
  388.  
  389.                     count--;
  390.                     Object oldValue = e.value;
  391.                     e.value = null;
  392.                     return oldValue;
  393.                 }
  394.             }
  395.         } else {
  396.             for (Entry e = tab[0], prev = null; e != null;
  397.                  prev = e, e = e.next) {
  398.                 if (e.key == null) {
  399.                     modCount++;
  400.                     if (prev != null)
  401.                         prev.next = e.next;
  402.                     else
  403.                         tab[0] = e.next;
  404.  
  405.                     count--;
  406.                     Object oldValue = e.value;
  407.                     e.value = null;
  408.                     return oldValue;
  409.                 }
  410.             }
  411.         }
  412.  
  413.     return null;
  414.     }
  415.  
  416.     /**
  417.      * Copies all of the mappings from the specified map to this one.
  418.      * 
  419.      * These mappings replace any mappings that this map had for any of the
  420.      * keys currently in the specified Map.
  421.      *
  422.      * @param t Mappings to be stored in this map.
  423.      */
  424.     public void putAll(Map t) {
  425.     Iterator i = t.entrySet().iterator();
  426.     while (i.hasNext()) {
  427.         Map.Entry e = (Map.Entry) i.next();
  428.         put(e.getKey(), e.getValue());
  429.     }
  430.     }
  431.  
  432.     /**
  433.      * Removes all mappings from this map.
  434.      */
  435.     public void clear() {
  436.     Entry tab[] = table;
  437.     modCount++;
  438.     for (int index = tab.length; --index >= 0; )
  439.         tab[index] = null;
  440.     count = 0;
  441.     }
  442.  
  443.     /**
  444.      * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
  445.      * values themselves are not cloned.
  446.      *
  447.      * @return a shallow copy of this map.
  448.      */
  449.     public Object clone() {
  450.     try { 
  451.         HashMap t = (HashMap)super.clone();
  452.         t.table = new Entry[table.length];
  453.         for (int i = table.length ; i-- > 0 ; ) {
  454.         t.table[i] = (table[i] != null) 
  455.             ? (Entry)table[i].clone() : null;
  456.         }
  457.         t.keySet = null;
  458.         t.entrySet = null;
  459.             t.values = null;
  460.         t.modCount = 0;
  461.         return t;
  462.     } catch (CloneNotSupportedException e) { 
  463.         // this shouldn't happen, since we are Cloneable
  464.         throw new InternalError();
  465.     }
  466.     }
  467.  
  468.     // Views
  469.  
  470.     private transient Set keySet = null;
  471.     private transient Set entrySet = null;
  472.     private transient Collection values = null;
  473.  
  474.     /**
  475.      * Returns a set view of the keys contained in this map.  The set is
  476.      * backed by the map, so changes to the map are reflected in the set, and
  477.      * vice-versa.  The set supports element removal, which removes the
  478.      * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
  479.      * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
  480.      * <tt>clear</tt> operations.  It does not support the <tt>add</tt> or
  481.      * <tt>addAll</tt> operations.
  482.      *
  483.      * @return a set view of the keys contained in this map.
  484.      */
  485.     public Set keySet() {
  486.     if (keySet == null) {
  487.         keySet = new AbstractSet() {
  488.         public Iterator iterator() {
  489.             return new HashIterator(KEYS);
  490.         }
  491.         public int size() {
  492.             return count;
  493.         }
  494.                 public boolean contains(Object o) {
  495.                     return containsKey(o);
  496.                 }
  497.         public boolean remove(Object o) {
  498.             return HashMap.this.remove(o) != null;
  499.         }
  500.         public void clear() {
  501.             HashMap.this.clear();
  502.         }
  503.         };
  504.     }
  505.     return keySet;
  506.     }
  507.  
  508.     /**
  509.      * Returns a collection view of the values contained in this map.  The
  510.      * collection is backed by the map, so changes to the map are reflected in
  511.      * the collection, and vice-versa.  The collection supports element
  512.      * removal, which removes the corresponding mapping from this map, via the
  513.      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  514.      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
  515.      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
  516.      *
  517.      * @return a collection view of the values contained in this map.
  518.      */
  519.     public Collection values() {
  520.     if (values==null) {
  521.         values = new AbstractCollection() {
  522.                 public Iterator iterator() {
  523.                     return new HashIterator(VALUES);
  524.                 }
  525.                 public int size() {
  526.                     return count;
  527.                 }
  528.                 public boolean contains(Object o) {
  529.                     return containsValue(o);
  530.                 }
  531.                 public void clear() {
  532.                     HashMap.this.clear();
  533.                 }
  534.             };
  535.         }
  536.     return values;
  537.     }
  538.  
  539.     /**
  540.      * Returns a collection view of the mappings contained in this map.  Each
  541.      * element in the returned collection is a <tt>Map.Entry</tt>.  The
  542.      * collection is backed by the map, so changes to the map are reflected in
  543.      * the collection, and vice-versa.  The collection supports element
  544.      * removal, which removes the corresponding mapping from the map, via the
  545.      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  546.      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
  547.      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
  548.      *
  549.      * @return a collection view of the mappings contained in this map.
  550.      * @see Map.Entry
  551.      */
  552.     public Set entrySet() {
  553.     if (entrySet==null) {
  554.         entrySet = new AbstractSet() {
  555.                 public Iterator iterator() {
  556.                     return new HashIterator(ENTRIES);
  557.                 }
  558.  
  559.                 public boolean contains(Object o) {
  560.                     if (!(o instanceof Map.Entry))
  561.                         return false;
  562.                     Map.Entry entry = (Map.Entry)o;
  563.                     Object key = entry.getKey();
  564.                     Entry tab[] = table;
  565.                     int hash = (key==null ? 0 : key.hashCode());
  566.                     int index = (hash & 0x7FFFFFFF) % tab.length;
  567.  
  568.                     for (Entry e = tab[index]; e != null; e = e.next)
  569.                         if (e.hash==hash && e.equals(entry))
  570.                             return true;
  571.                     return false;
  572.                 }
  573.  
  574.         public boolean remove(Object o) {
  575.                     if (!(o instanceof Map.Entry))
  576.                         return false;
  577.                     Map.Entry entry = (Map.Entry)o;
  578.                     Object key = entry.getKey();
  579.                     Entry tab[] = table;
  580.                     int hash = (key==null ? 0 : key.hashCode());
  581.                     int index = (hash & 0x7FFFFFFF) % tab.length;
  582.  
  583.                     for (Entry e = tab[index], prev = null; e != null;
  584.                          prev = e, e = e.next) {
  585.                         if (e.hash==hash && e.equals(entry)) {
  586.                             modCount++;
  587.                             if (prev != null)
  588.                                 prev.next = e.next;
  589.                             else
  590.                                 tab[index] = e.next;
  591.  
  592.                             count--;
  593.                             e.value = null;
  594.                             return true;
  595.                         }
  596.                     }
  597.                     return false;
  598.                 }
  599.  
  600.                 public int size() {
  601.                     return count;
  602.                 }
  603.  
  604.                 public void clear() {
  605.                     HashMap.this.clear();
  606.                 }
  607.             };
  608.         }
  609.  
  610.     return entrySet;
  611.     }
  612.  
  613.     /**
  614.      * HashMap collision list entry.
  615.      */
  616.     private static class Entry implements Map.Entry {
  617.     int hash;
  618.     Object key;
  619.     Object value;
  620.     Entry next;
  621.  
  622.     Entry(int hash, Object key, Object value, Entry next) {
  623.         this.hash = hash;
  624.         this.key = key;
  625.         this.value = value;
  626.         this.next = next;
  627.     }
  628.  
  629.     protected Object clone() {
  630.         return new Entry(hash, key, value,
  631.                  (next==null ? null : (Entry)next.clone()));
  632.     }
  633.  
  634.     // Map.Entry Ops 
  635.  
  636.     public Object getKey() {
  637.         return key;
  638.     }
  639.  
  640.     public Object getValue() {
  641.         return value;
  642.     }
  643.  
  644.     public Object setValue(Object value) {
  645.         Object oldValue = this.value;
  646.         this.value = value;
  647.         return oldValue;
  648.     }
  649.  
  650.     public boolean equals(Object o) {
  651.         if (!(o instanceof Map.Entry))
  652.         return false;
  653.         Map.Entry e = (Map.Entry)o;
  654.  
  655.         return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  656.            (value==null ? e.getValue()==null : value.equals(e.getValue()));
  657.     }
  658.  
  659.     public int hashCode() {
  660.         return hash ^ (value==null ? 0 : value.hashCode());
  661.     }
  662.  
  663.     public String toString() {
  664.         return key.toString()+"="+value.toString();
  665.     }
  666.     }
  667.  
  668.     // Types of Iterators
  669.     private static final int KEYS = 0;
  670.     private static final int VALUES = 1;
  671.     private static final int ENTRIES = 2;
  672.  
  673.     private class HashIterator implements Iterator {
  674.     Entry[] table = HashMap.this.table;
  675.     int index = table.length;
  676.     Entry entry = null;
  677.     Entry lastReturned = null;
  678.     int type;
  679.  
  680.     /**
  681.      * The modCount value that the iterator believes that the backing
  682.      * List should have.  If this expectation is violated, the iterator
  683.      * has detected concurrent modification.
  684.      */
  685.     private int expectedModCount = modCount;
  686.  
  687.     HashIterator(int type) {
  688.         this.type = type;
  689.     }
  690.  
  691.     public boolean hasNext() {
  692.         while (entry==null && index>0)
  693.         entry = table[--index];
  694.  
  695.         return entry != null;
  696.     }
  697.  
  698.     public Object next() {
  699.         if (modCount != expectedModCount)
  700.         throw new ConcurrentModificationException();
  701.  
  702.         while (entry==null && index>0)
  703.         entry = table[--index];
  704.  
  705.         if (entry != null) {
  706.         Entry e = lastReturned = entry;
  707.         entry = e.next;
  708.         return type == KEYS ? e.key : (type == VALUES ? e.value : e);
  709.         }
  710.         throw new NoSuchElementException();
  711.     }
  712.  
  713.     public void remove() {
  714.         if (lastReturned == null)
  715.         throw new IllegalStateException();
  716.         if (modCount != expectedModCount)
  717.         throw new ConcurrentModificationException();
  718.  
  719.         Entry[] tab = HashMap.this.table;
  720.         int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
  721.  
  722.         for (Entry e = tab[index], prev = null; e != null;
  723.          prev = e, e = e.next) {
  724.         if (e == lastReturned) {
  725.             modCount++;
  726.             expectedModCount++;
  727.             if (prev == null)
  728.             tab[index] = e.next;
  729.             else
  730.             prev.next = e.next;
  731.             count--;
  732.             lastReturned = null;
  733.             return;
  734.         }
  735.         }
  736.         throw new ConcurrentModificationException();
  737.     }
  738.     }
  739.  
  740.     /**
  741.      * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
  742.      * serialize it).
  743.      *
  744.      * @serialData The <i>capacity</i> of the HashMap (the length of the
  745.      *           bucket array) is emitted (int), followed  by the
  746.      *           <i>size</i> of the HashMap (the number of key-value
  747.      *           mappings), followed by the key (Object) and value (Object)
  748.      *           for each key-value mapping represented by the HashMap
  749.      * The key-value mappings are emitted in no particular order.
  750.      */
  751.     private void writeObject(java.io.ObjectOutputStream s)
  752.         throws IOException
  753.     {
  754.     // Write out the threshold, loadfactor, and any hidden stuff
  755.     s.defaultWriteObject();
  756.  
  757.     // Write out number of buckets
  758.     s.writeInt(table.length);
  759.  
  760.     // Write out size (number of Mappings)
  761.     s.writeInt(count);
  762.  
  763.         // Write out keys and values (alternating)
  764.     for (int index = table.length-1; index >= 0; index--) {
  765.         Entry entry = table[index];
  766.  
  767.         while (entry != null) {
  768.         s.writeObject(entry.key);
  769.         s.writeObject(entry.value);
  770.         entry = entry.next;
  771.         }
  772.     }
  773.     }
  774.  
  775.     /**
  776.      * Reconstitute the <tt>HashMap</tt> instance from a stream (i.e.,
  777.      * deserialize it).
  778.      */
  779.     private void readObject(java.io.ObjectInputStream s)
  780.          throws IOException, ClassNotFoundException
  781.     {
  782.     // Read in the threshold, loadfactor, and any hidden stuff
  783.     s.defaultReadObject();
  784.  
  785.     // Read in number of buckets and allocate the bucket array;
  786.     int numBuckets = s.readInt();
  787.     table = new Entry[numBuckets];
  788.  
  789.     // Read in size (number of Mappings)
  790.     int size = s.readInt();
  791.  
  792.     // Read the keys and values, and put the mappings in the HashMap
  793.     for (int i=0; i<size; i++) {
  794.         Object key = s.readObject();
  795.         Object value = s.readObject();
  796.         put(key, value);
  797.     }
  798.     }
  799.  
  800.     int capacity() {
  801.         return table.length;
  802.     }
  803.  
  804.     float loadFactor() {
  805.         return loadFactor;
  806.     }
  807. }
  808.